<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>性能测试</title>
    <!-- modern browser -->
    <script src="https://cdn.tailwindcss.com"></script>

    <!-- ie 11 -->
    <!-- <script src="https://unpkg.com/babel-polyfill@6.26.0/dist/polyfill.min.js"></script>
  <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet"> -->
    <style>
      table,
      th,
      td {
        border: 1px solid #ccc;
      }

      table {
        border-collapse: collapse;
        border-spacing: 0;
        width: 100%;
      }

      th,
      td {
        padding: 2px 5px;
      }

      thead th {
        background-color: #ddd;
        white-space: nowrap;
      }

      tbody th,
      td {
        word-wrap: break-word;
        word-break: break-all;
      }

      td {
        text-align: right;
      }

      tr.fastest td {
        background-color: #e60000;
        color: #fff;
      }

      tr:nth-child(6n + 1),
      tr:nth-child(6n + 2),
      tr:nth-child(6n + 3) {
        background-color: #f0f0f0;
      }
    </style>
  </head>

  <body>
    <div class="w-full p-5 mx-auto">
      <h1 class="text-2xl mb-2">性能测试</h1>
      <p class="mb-2">使用 benchmark 测试 ut2 对比 underscore 和 lodash 的性能。</p>
      <p class="text-sm text-gray-500">工具库版本：<span id="lib-version"></span></p>
      <div class="my-5">选择方法：<span id="form"></span></div>
      <div id="action" class="mb-5"><button class="p-2 bg-purple-600 hover:bg-purple-500 rounded-lg text-white shadow-md">点击运行测试</button></div>
      <p id="tip-running" class="text-lg text-gray-600 mb-5" style="display: none">运行中...</p>
      <p id="tip-cache" class="text-sm text-gray-600 my-5" style="display: none">当前读取 <span id="tip-cache-date"></span> 运行结果，点击上面按钮可重新运行。</p>
      <div id="result" style="display: none">
        <table class="border font-mono">
          <thead>
            <tr>
              <!-- <th>方法</th> -->
              <th>测试用例</th>
              <th>库</th>
              <th>实际调用</th>
              <th>结果</th>
              <th>每秒执行次数</th>
              <th>波动范围</th>
              <th>抽样次数</th>
            </tr>
          </thead>
          <tbody></tbody>
        </table>
        <p class="my-5 text-xs text-gray-600"><input type="color" value="#E60000" class="align-middle" disabled /> 表示执行最快的。受运行环境和参数，以及部分方法功能不一样等影响，结果仅供参考。</p>
      </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.6/underscore-umd-min.js"></script>
    <script>
      var underscore = _.noConflict();
    </script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
    <script src="https://unpkg.com/platform@1/platform.js"></script>
    <script src="https://unpkg.com/benchmark@2/benchmark.js"></script>
    <script src="../dist/ut2.min.js"></script>
    <script src="https://unpkg.com/cache2@3/dist/cache2.min.js"></script>
    <script src="https://unpkg.com/dayjs@1/dayjs.min.js"></script>
    <script src="./fastest.js"></script>
    <script>
      const { Cache } = cache2;
      const categoryConfig = fastest.config;
      const cache = new Cache('benchmark', {
        storage: window.localStorage
      });
      const selectedCacheKey = '__selected__';

      // 添加 select
      const oForm = document.getElementById('form');
      const oSelect = document.createElement('select');
      categoryConfig.forEach(function (item) {
        const oOptgroup = document.createElement('optgroup');
        oOptgroup.setAttribute('label', item.category);

        item.list.forEach(function (subItem) {
          const oOption = document.createElement('option');
          oOption.setAttribute('value', subItem.method);
          oOption.innerHTML = subItem.method;

          oOptgroup.appendChild(oOption);
        });

        oSelect.appendChild(oOptgroup);
      });
      const cacheData = cache.get(selectedCacheKey);
      oSelect.value = cacheData || categoryConfig[0].list[0].method;
      oForm.appendChild(oSelect);

      oSelect.addEventListener('change', function (e) {
        cache.set(selectedCacheKey, e.target.value);
        renderResultWithCache();
      });

      // 各工具库版本
      const oLibVersion = document.getElementById('lib-version');
      oLibVersion.innerHTML = 'ut2@' + ut2.VERSION + ', lodash@' + _.VERSION + ', underscore@' + underscore.VERSION;

      // 测试
      const libs = ['underscore', 'lodash', 'ut2'];
      const oResult = document.getElementById('result');
      const oTbody = oResult.querySelector('tbody');
      const oAction = document.getElementById('action');
      const oRunButton = oAction.querySelector('button');
      const oTipRunning = document.getElementById('tip-running');
      const oTipCache = document.getElementById('tip-cache');
      const oTipCacheDate = document.getElementById('tip-cache-date');

      oRunButton.addEventListener('click', function () {
        runTest();
      });

      function runTest() {
        const method = oSelect.value;

        if (!method) {
          alert('请选择测试方法');
          return;
        }

        oTbody.innerHTML = '';
        oResult.style.display = 'none';
        oAction.style.display = 'none';
        oTipRunning.style.display = 'block';
        oTipCache.style.display = 'none';
        cache.del(method);

        setTimeout(function () {
          const result = fastest({
            Benchmark: Benchmark,
            ut2: ut2,
            underscore: underscore,
            lodash: _,
            methods: method
          })[method];

          cache.set(method, result);
          oAction.style.display = 'block';
          oTipRunning.style.display = 'none';

          renderResult(result);
        }, 100);
      }

      function renderResult(data) {
        const fragment = document.createDocumentFragment();

        data.forEach(function (item, index) {
          libs.forEach(function (libItem, libIndex) {
            const tr = document.createElement('tr');

            if (libItem === item.$fastest) {
              tr.setAttribute('class', 'fastest');
            }

            if (libIndex === 0) {
              const th = document.createElement('th');
              th.setAttribute('rowspan', 3);
              th.innerHTML = item.$case;
              tr.appendChild(th);
            }
            const td0 = document.createElement('td');
            const td1 = document.createElement('td');
            const td2 = document.createElement('td');
            const td3 = document.createElement('td');
            const td4 = document.createElement('td');
            const td5 = document.createElement('td');

            td0.innerHTML = '<div style="white-space: nowrap;">' + libItem + '</div>';
            td1.innerHTML = item[libItem].existed ? item[libItem].case : 'unsupported';
            td2.innerHTML = item[libItem].existed ? JSON.stringify(item[libItem].result) : '-';
            td3.innerHTML = item[libItem].existed ? item[libItem].hz : '-';
            td4.innerHTML = item[libItem].existed ? item[libItem].rme : '-';
            td5.innerHTML = item[libItem].existed ? item[libItem].sampled : '-';

            tr.appendChild(td0);
            tr.appendChild(td1);
            tr.appendChild(td2);
            tr.appendChild(td3);
            tr.appendChild(td4);
            tr.appendChild(td5);

            fragment.appendChild(tr);
          });
        });

        oTbody.innerHTML = '';
        oTbody.appendChild(fragment);
        oResult.style.display = 'block';
      }

      function renderResultWithCache() {
        const method = oSelect.value;
        if (!method) {
          return;
        }

        const cacheData = cache.get(method);
        if (!cacheData) {
          oTipRunning.style.display = 'none';
          oTipCache.style.display = 'none';
          oTbody.innerHTML = '';
          oResult.style.display = 'none';
          return;
        }

        oTipRunning.style.display = 'none';
        oTipCache.style.display = 'block';
        oTipCacheDate.innerHTML = dayjs(cache.getLastModified(method)).format('YYYY-MM-DD HH:mm:ss');

        renderResult(cacheData);
      }

      function init() {
        renderResultWithCache();
      }

      init();
    </script>
  </body>
</html>
